class LogicAnalyzer { constructor( cc, style, doNotStartFrame ) { this.cc = cc; this.style = Object.assign( { //表示スタイル指定 titleFontSize : 10, //左端のチャートタイトルのフォントサイズ commentFontSize : 10, //コメントのフォントサイズ cellHeight : 32, //各チャート領域の高さ palseHeight : 8, //パルスの表示高さ horizontalDiv : 8, //TIME/DIV 横方向を1/nの大きさで表示する(圧縮度) horizontalShift : 32, //チャートを左右に動かす //top : 0, //指定すると、CANVASの上からの位置 //bottom : 0, //指定すると、CANVASの下からの位置 //trigChart : "test", //指定すると、そのチャートの最初の記録を基準に画面描画 }, style ); this.etc = new Object(); this.etc.title = "JavaScript LOGIC ANALYZER by web6047"; cc.font = this.style.titleFontSize + "px''"; this.etc.titleX = cc.canvas.width - cc.measureText( this.etc.title ).width - this.style.titleFontSize; this.records = new Object(); this.cursorLeftTime = 0; this.time = 0; this.maxTime = 100; //画面更新頻度ms CPU使用率を下げたいときに増やす。波形の精度に影響しない。 this.backTm = 0; if( ! doNotStartFrame ) this.frame( 0 ); } log( name, hiLow, comment ) { //check. 配列の作成 if( ! this.records[ name ] ) this.records[ name ] = new Array(); //check. 同じ動きは記録しない。 let lastRecord = this.records[ name ][ this.records[ name ].length - 1 ]; if( lastRecord && lastRecord[ 1 ] == hiLow ) return; let now = Date.now(); this.records[ name ].push( [ now, hiLow, comment ] ); } frame( tm ) { let diff = tm - this.backTm; this.backTm = tm; this.time += diff; if( this.time >= this.maxTime ) { this.time = 0; this.draw( this.cc ); } if( this.autoFlg ) this.trig(); requestAnimationFrame( this.frame.bind( this ) ); } auto() { this.autoFlg = true; this.style.horizontalShift = this.cc.canvas.width; } stop() { setTimeout( function() { this.trig(); this.records = new Object(); if( this.style.trigChart ) this.cursorLeftTime = this.lines[ this.style.trigChart ][ 0 ][ 0 ]; else this.cursorLeftTime = this.lines[ Object.keys( this.lines )[ 0 ] ][ 0 ][ 0 ]; //参考: // this.lines[ チャートタイトル ][ 何番目の記録 ][ 0:記録したときの経過時間 ] }.bind( this ), 1000 ); } trig() { let now = Date.now(); this.lines = new Object(); //各チャートについて for( let name in this.records ) { let record = this.records[ name ]; let drawPoints = new Array(); //2次元配列 [ [ x, y ], ... ] //x 下記yが起こった時間から現在時刻までの経過時間 //y 1:信号の立ち上がり、0:立ち下がり //そのチャートの、各記録について for( let j = 0; j < record.length; j++ ) { let sample = record[ j ]; let recordTime = sample[ 0 ]; let method = sample[ 1 ]; let comment = sample[ 2 ]; let leftTime = now - recordTime; //波形にパルスをセット if( method == 1 ) { //立ち上がり drawPoints.push( [ leftTime, 0 ] ); drawPoints.push( [ leftTime, 1, comment ] ); } else { //立ち下がり //check. 画面左端に来たらパルスを削除予定(null) if( leftTime < 0 ) { record[ j ] = null; record[ j - 1 ] = null; } drawPoints.push( [ leftTime, 1 ] ); drawPoints.push( [ leftTime, 0, comment ] ); } }//for record[] //check. 削除実行 for( let j = record.length - 1; j >= 0; j-- ) { if( record[ j ] === null ) record.splice( j, 1 ); } this.lines[ name ] = drawPoints; }//for this.records[] } draw( cc ) { cc.save(); cc.lineWidth = 0.5; let height; if( this.lines ) { height = ( Object.keys( this.lines ).length + 1) * this.style.cellHeight; } else { height = this.style.cellHeight; } let top = 0; //check. if( this.style.top != undefined ) top = this.style.top; else if( this.style.bottom != undefined ) top = cc.canvas.height - this.style.bottom -height; cc.translate( 0, top ); cc.fillStyle = "black"; cc.fillRect( 0, 0, cc.canvas.width, height ); cc.strokeStyle = "lightgray"; cc.strokeRect( 0, 0, cc.canvas.width, height ); cc.font = this.style.titleFontSize + "px''"; cc.fillStyle = "magenta"; cc.fillText( this.etc.title, this.etc.titleX, this.style.titleFontSize ); //check. if( ! this.lines ) { let sz = 16; cc.font = sz + "px''"; cc.fillText( "Trig?", sz, sz ); } else { let i = 0; for( let name in this.lines ) { let drawPoints = this.lines[ name ]; cc.beginPath(); let gyBase = ( i + 1 ) * this.style.cellHeight; cc.moveTo( 0, gyBase ); for( let drawPoint of drawPoints ) { let leftTime = drawPoint[ 0 ]; let hiLow = drawPoint[ 1 ]; let comment = drawPoint[ 2 ]; let gx = ( leftTime - this.cursorLeftTime ) * -1 / this.style.horizontalDiv; gx += this.style.horizontalShift; let gy = gyBase - hiLow * this.style.palseHeight; cc.lineTo( gx, gy ); if( comment != null ) { cc.save(); cc.font = this.style.commentFontSize + "px''"; gx = gx - this.style.commentFontSize / 2; cc.fillStyle = "cyan"; if( hiLow ) { gy = gyBase - ( this.style.palseHeight + 3 ); cc.fillText( "↓" + comment, gx, gy ); } else { gy = gyBase + ( this.style.palseHeight + 3 ); cc.fillText( "↑" + comment, gx, gy ); } cc.restore(); } } //画面右端まで描画 if( drawPoints.length / 2 % 2 == 1 ) { //記録数が奇数なら、記録の最後はハイ cc.lineTo( cc.canvas.width, gyBase - this.style.palseHeight ); } else { //記録数が偶数なら、記録の最後はロー cc.lineTo( cc.canvas.width, gyBase ); } cc.strokeStyle = "cyan"; cc.stroke(); cc.fillStyle = "yellow"; cc.fillText( name + "↓", 4, gyBase - this.style.titleFontSize * 2 ); i++; }//for }//if cc.restore(); } }